package com.sann.eduservice.service.impl;

import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sann.common.utils.ResultCode;
import com.sann.eduservice.entity.subject.Subject;
import com.sann.eduservice.entity.subject.vo.SubjectData;
import com.sann.eduservice.entity.subject.vo.OneSubject;
import com.sann.eduservice.entity.subject.vo.TwoSubject;
import com.sann.eduservice.listener.SubjectExcelListener;
import com.sann.eduservice.mapper.SubjectMapper;
import com.sann.eduservice.service.SubjectService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sann.servicebase.handler.exception.GuliException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 课程科目 服务实现类
 * </p>
 *
 * @author zjw
 * @since 2021-04-14
 */
@Service
@Slf4j
public class SubjectServiceImpl extends ServiceImpl<SubjectMapper, Subject> implements SubjectService {

    /**
     * 导入课程分类
     */
    @Override
    public void importSubject(MultipartFile file, SubjectService subjectService) {
        try {
            InputStream in = file.getInputStream();
            //调用方法进行读取
            EasyExcel.read(in, SubjectData.class, new SubjectExcelListener(subjectService)).sheet().doRead();
        }catch (Exception e){
//            log.error(e.getMessage());
            e.printStackTrace();
            throw new GuliException(ResultCode.ERROR, "读取Excel文件出错了");
        }
    }

    /**
     * 获取所有课程科目，并以树形结构返回
     *
     * TODO this.list() 是如何做到的?
     * 分析：
     * 1、ServiceImpl.baseMapper 在 ServiceImpl 初始化时 通过 @Autowired protected M baseMapper 注入：
     *    我们的 SubjectServiceImpl 继承了 ServiceImpl，因此Spring在实例化我们的 SubjectServiceImpl前就实例化了ServiceImpl，
     *    也就是说，我们的 SubjectMapper 被实例化并赋值给了变量 ServiceImpl.baseMapper。(BaseMapper就是DAO，Spring会创建DAO代理去操作数据库)
     * 2、this.list()是因 ServiceImpl implements IService 接口，IService里面定义了list()接口方法(list()有默认实现)：
     *    SubjectServiceImpl extends ServiceImpl，就继承有了list()方法。（implements SubjectService只是为了定义更多我们自己的业务接口方法）
     * 3、this.list(wrapper)怎么就和baseMapper.selectList(wrapper)调用的是同一个Mapper方法的？
     *    this.list(wrapper)是通过IService的list()默认实现，内部 return getBaseMapper().selectList(queryWrapper);，
     *    通过 getBaseMapper() 拿到 BaseMapper，而这个BaseMapper正是 IService内部的 BaseMapper<T> getBaseMapper();抽象方法，
     *    ServiceImpl 是 IService 的实现，ServiceImpl 对 getBaseMapper()的实现
     *    @Override
     *     public M getBaseMapper() {
     *         return baseMapper;
     *     }
     *     返回的正是 ServiceImpl 中 @Autowired注入进来的 BaseMapper。
     *     @Autowired
     *     protected M baseMapper;
     *     也就是说，这两种方式最终调用的都是这个BaseMapper.selectList()方法。
     *
     * 4、总结：ServiceImpl内部自动注入BaseMapper，通过BaseMapper这个DAO操作数据库。
     *        IService内部的list()方法默认实现是通过获取唯一实现类ServiceImpl的BaseMapper，然后通过这个BaseMapper操作数据库
     *
     */
    @Override
    public List<OneSubject> getAllOneTwoSubjectTree() {
        //1 查询所有一级分类  parentid = 0
        QueryWrapper<Subject> wrapperOne = new QueryWrapper<>();
        wrapperOne.eq("parent_id", "0");
        List<Subject> dbSubjectList1 = baseMapper.selectList(wrapperOne);
        // TODO this.list() 是如何做到的?
//        this.list(wrapperOne);//也可以查询所有一级分类

        //2 查询所有二级分类  parentid != 0
        QueryWrapper<Subject> wrapperTwo = new QueryWrapper<>();
        wrapperTwo.ne("parent_id", "0");
        List<Subject> dbSubjectList2 = baseMapper.selectList(wrapperTwo);

        // 最终要返回的tree
        List<OneSubject> tree = new ArrayList<>();

        //3 封装一级分类
        for (Subject subject1 : dbSubjectList1) {
            OneSubject oneSubject = new OneSubject();
            // 通过Spring提供的工具类将subject属性copy到oneSubject（根据同名属性copy）
            BeanUtils.copyProperties(subject1, oneSubject);
            tree.add(oneSubject);


            /**
             * 在一级分类循环遍历查询其下包含的所有二级分类。
             */
            // 封装一级分类下的二级分类
            List<TwoSubject> twoTree = new ArrayList<>();
            for (Subject subject2 : dbSubjectList2) {
                if (subject1.getId().equals(subject2.getParentId())) {
                    TwoSubject twoSubject = new TwoSubject();
                    BeanUtils.copyProperties(subject2, twoSubject);
                    twoTree.add(twoSubject);
                }
            }

            oneSubject.setChildren(twoTree);
        }

        return tree;
    }
}
